package com.xing.yunji.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xing.yunji.common.ErrorCode;
import com.xing.yunji.exception.BusinessException;
import com.xing.yunji.mapper.TeamMapper;
import com.xing.yunji.model.domain.Team;
import com.xing.yunji.model.domain.User;
import com.xing.yunji.model.domain.UserTeam;
import com.xing.yunji.model.dto.TeamQuery;
import com.xing.yunji.model.enums.TeamTypeEnum;
import com.xing.yunji.model.request.team.TeamJoinRequest;
import com.xing.yunji.model.request.team.TeamQuitRequest;
import com.xing.yunji.model.request.team.TeamUpdateRequest;
import com.xing.yunji.model.vo.TeamUserVO;
import com.xing.yunji.model.vo.UserVO;
import com.xing.yunji.service.TeamService;
import com.xing.yunji.service.UserService;
import com.xing.yunji.service.UserTeamService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 17916
 * @description 针对表【team(用户表)】的数据库操作Service实现
 * @createDate 2023-04-08 12:06:32
 */
@Service
public class TeamServiceImpl extends ServiceImpl<TeamMapper, Team>
        implements TeamService {


    @Resource
    private UserService userService;

    @Resource
    private UserTeamService userTeamService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public long addTeam(Team team, User loginUser) {
        //1. 请求参数是否为空
        if (team == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //2. 是否登录，未登录，不允许创建
        if (loginUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN);
        }
        //3. 校验信息
        //   1. 队伍人数>1 且 <=20
        if (team.getMaxNum() < 1 || team.getMaxNum() > 20) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍人数不满足要求");
        }
        //   2. 队伍标题 <=20
        if (team.getName().length() > 20) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍标题过长");
        }
        //   3. 描述 <=512  (不为空 && 长度>512 不满足)
        String description = team.getDescription();
        if (StringUtils.isNotBlank(description) && description.length() > 512) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍描述过长");
        }
        //   4. type 是否为公开(int) 不传默认为0（公开）
        Integer type = Optional.ofNullable(team.getType()).orElse(0);
        TeamTypeEnum typeEnum = TeamTypeEnum.getEnumByValue(type);
        if (typeEnum == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍状态不满足要求");
        }
        //   5. 如果 type 是加密，一定要有密码，且长度 <=32
        String password = team.getPassword();
        if (TeamTypeEnum.SECRET == typeEnum) {
            if (StringUtils.isBlank(password) || password.length() > 32) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码设置不满足要求");
            }
        }
        //   6. 超时时间 > 当前时间
        Date expireTime = team.getExpireTime();
        if (expireTime == null) {
            // 设置 1 个月过期
            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.MONTH, 1);
            expireTime = calendar.getTime();
        } else if (new Date().after(expireTime)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "超时时间不能早于当前时间");
        }
        //   7. 校验用户最多创建 5 个队伍
        QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", loginUser.getId());
        long hasTeamNum = userTeamService.count(queryWrapper);
        if (hasTeamNum >= 5) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户最多创建 5 个队伍");
        }
        //4. 插入队伍信息到队伍表
        team.setId(null);
        team.setUserId(loginUser.getId());
        team.setLeaderId(loginUser.getId());
        team.setExpireTime(expireTime);
        boolean save = this.save(team);
        Long teamId = team.getId();
        if (!save || teamId == null) {
            throw new BusinessException(ErrorCode.SYSTEM_EXCEPTION, "创建队伍失败");
        }
        //5. 插入 用户-队伍 信息 到关系表
        UserTeam userTeam = new UserTeam();
        userTeam.setUserId(loginUser.getId());
        userTeam.setTeamId(teamId);
        userTeam.setJoinTime(new Date());
        boolean result = userTeamService.save(userTeam);
        if (!result) {
            throw new BusinessException(ErrorCode.SYSTEM_EXCEPTION, "创建队伍失败");
        }
        return teamId;
    }

    @Override
    public boolean updateTeam(TeamUpdateRequest teamUpdateRequest, User loginUser) {
        //1. 请求参数是否为空
        if (teamUpdateRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //2. 队伍是否存在
        Long teamId = teamUpdateRequest.getId();
        Team oldTeam = this.getOne(new QueryWrapper<Team>().eq("id", teamId));
        if (oldTeam == null) {
            throw new BusinessException(ErrorCode.SYSTEM_EXCEPTION, "要修改的队伍不存在！");
        }
        //3. 只有管理员，或者队长可以修改
        if (!oldTeam.getLeaderId().equals(loginUser.getId()) && !userService.isAdmin(loginUser)) {
            throw new BusinessException(ErrorCode.NO_AUTH, "只有队长或管理员才可以修改队伍！");
        }
        //4. todo 如果用户传入的新值和老值一致，那么就不用update，减少数据库使用次数
        //5. 如果队伍状态改为加密，必须要有密码
        TeamTypeEnum enumByValue = TeamTypeEnum.getEnumByValue(teamUpdateRequest.getType());
        String password = teamUpdateRequest.getPassword();
        if (TeamTypeEnum.SECRET.equals(enumByValue)) {
            if (StringUtils.isBlank(password) || password.length() > 32) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "加密房间必须要设置密码！且密码长度不能超过32！");
            }
        }
        Team team = new Team();
        BeanUtils.copyProperties(teamUpdateRequest, team);
        return this.updateById(team);
    }

    @Override
    public List<TeamUserVO> listTeams(TeamQuery teamQuery, boolean isAdmin) {

        // 1. 请求参数中取出队伍名称等，作为查询条件
        if (teamQuery == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Long id = teamQuery.getId();
        String searchText = teamQuery.getSearchText();
        String name = teamQuery.getName();
        String description = teamQuery.getDescription();
        Long userId = teamQuery.getUserId();
        Long leaderId = teamQuery.getLeaderId();
        Integer type = teamQuery.getType();
        Integer maxNum = teamQuery.getMaxNum();
        LambdaQueryWrapper<Team> lqw = new LambdaQueryWrapper<>();
        if (id != null && id > 0) {
            lqw.eq(Team::getId, id);
        }
        List<Long> idList = teamQuery.getIdList();
        if (CollectionUtils.isNotEmpty(idList)) {
            lqw.in(Team::getId, idList);
        }
        if (StringUtils.isNotBlank(searchText)) {
            lqw.and(qw -> qw.like(Team::getName, searchText).or().like(Team::getDescription, searchText));
        }
        if (StringUtils.isNotBlank(name)) {
            lqw.like(Team::getName, name);
        }
        if (StringUtils.isNotBlank(description)) {
            lqw.like(Team::getDescription, description);
        }
        if (maxNum != null && maxNum > 0) {
            lqw.eq(Team::getMaxNum, maxNum);
        }
        // 根据创建人来查询
        if (userId != null && userId > 0) {
            lqw.eq(Team::getUserId, userId);
        }
        if (leaderId != null && leaderId > 0) {
            lqw.eq(Team::getLeaderId, leaderId);
        }
        // 根据队伍类型来查询
/*
        List<TeamTypeEnum> teamTypeEnumsList = new ArrayList<>(Arrays.asList(TeamTypeEnum.getPublic(),
                TeamTypeEnum.getPrivate(),
                TeamTypeEnum.getSecret()));
        // 不是admin不能查看私密的房间
        if (!isAdmin) {
            teamTypeEnumsList.remove(TeamTypeEnum.PRIVATE);
        }
        List<Integer> typeList = teamTypeEnumsList.stream().map(TeamTypeEnum::getValue).collect(Collectors.toList());
*/
        if (type == null) {
            List<Integer> typeEnumValues = typeEnumValues = TeamTypeEnum.getTeamTypeEnumValues();
            lqw.in(Team::getType, typeEnumValues);
        }else {
            lqw.eq(Team::getType,type);
        }

        // 不展示已经过期的队伍
        lqw.and(qw -> qw.gt(Team::getExpireTime, new Date()).or().isNull(Team::getExpireTime));
        // 关联查询已加入队伍的用户信息
        List<Team> teamList = this.list(lqw);
        if (CollectionUtils.isEmpty(teamList)) {
            return new ArrayList<>();
        }
        List<TeamUserVO> teamUserVOList = new ArrayList<>();
        for (Team team : teamList) {
            TeamUserVO teamUserVO = new TeamUserVO();
            BeanUtils.copyProperties(team, teamUserVO);
            Long teamLeaderId = team.getLeaderId();
            if (teamLeaderId == null) {
                continue;
            }
            User leader = userService.getById(teamLeaderId);
            // 脱敏用户信息
            if (leader != null) {
                UserVO userVO = new UserVO();
                BeanUtils.copyProperties(leader, userVO);
                teamUserVO.setLeader(userVO);
            }
            teamUserVOList.add(teamUserVO);
        }
        return teamUserVOList;
    }

    @Override
    public boolean joinTeam(TeamJoinRequest teamJoinRequest, HttpServletRequest request) {
        User loginUser = userService.getLoginUser(request);
        // 1. 最多加入 5 个
        Long userId = loginUser.getId();
        long teamCount = userTeamService.count(new LambdaQueryWrapper<UserTeam>().eq(UserTeam::getUserId, userId));
        if (teamCount >= 5) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "1 个用户最多加入 5 个队伍！");
        }
        // 2. 队伍必须存在，
        Long teamId = teamJoinRequest.getTeamId();
        Team team = getTeamById(teamId);
        // 队伍必须未过期
        Date expireTime = team.getExpireTime();
        if (expireTime.before(new Date())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "该队伍已过期！");
        }
        // 只能加入未满的
        Integer maxNum = team.getMaxNum();
        long userCount = userTeamService.count(new LambdaQueryWrapper<UserTeam>().eq(UserTeam::getTeamId, teamId));
        if (userCount >= maxNum) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "该队伍人数已满！");
        }
        // 3. 不能加入自己的队伍，不能重复加入已加入的队伍
        Long leaderId = team.getLeaderId();
        if (leaderId.equals(userId)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "您是当前队伍的队长，已经加入该队伍！");
        }
        // 4. 不能加入私有的队伍
        TeamTypeEnum enumByValue = TeamTypeEnum.getEnumByValue(team.getType());
        if (enumByValue.equals(TeamTypeEnum.PRIVATE)) {
            throw new BusinessException(ErrorCode.NO_AUTH, "不能加入私有的队伍！");
        }
        // 5. 如果队伍是加密的，必须密码匹配
        if (enumByValue.equals(TeamTypeEnum.SECRET)) {
            if (!teamJoinRequest.getPassword().equals(team.getPassword())) {
                throw new BusinessException(ErrorCode.NO_AUTH, "入队密码不正确！");
            }
        }
        // 6. 新增 队伍 - 用户 关联信息
        UserTeam userTeam = new UserTeam();
        userTeam.setUserId(userId);
        userTeam.setTeamId(teamId);
        userTeam.setJoinTime(new Date());
        return userTeamService.save(userTeam);
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean quitTeam(TeamQuitRequest teamQuitRequest, HttpServletRequest request) {
        // 队伍是否存在
        Long teamId = teamQuitRequest.getTeamId();
        Team team = getTeamById(teamId);
        // 我是否已加入该队伍
        User loginUser = userService.getLoginUser(request);
        Long userId = loginUser.getId();
        long count = userTeamService.count(new LambdaQueryWrapper<UserTeam>()
                .eq(UserTeam::getTeamId, teamId).eq(UserTeam::getUserId, userId));
        if (count <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "您未加入该队伍！");
        }
        // 如果队伍
        long userCount = countUserByTeamId(teamId);
        //   - 只剩一人，队伍解散（删除team_user记录，和team记录）
        if (userCount == 1) {
            this.removeById(teamId);
        }
        //   - 还有其他人
        //     - 如果自己是队长，那么设置第二早加入队伍的用户为队长（根据加入时间，或者id来判断）
        if (userCount > 1) {
            if (team.getLeaderId().equals(userId)) {
                QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();
                userTeamQueryWrapper.eq("teamId", teamId);
                userTeamQueryWrapper.last("order by id asc limit 2");
                List<UserTeam> userTeamList = userTeamService.list(userTeamQueryWrapper);
                if (CollectionUtils.isEmpty(userTeamList) || userTeamList.size() <= 1) {
                    throw new BusinessException(ErrorCode.SYSTEM_EXCEPTION);
                }
                UserTeam nextUserTeam = userTeamList.get(1);
                Long nextTeamLeaderId = nextUserTeam.getUserId();
                // 更新当前队伍的队长
                Team updateTeam = new Team();
                updateTeam.setId(teamId);
                updateTeam.setLeaderId(nextTeamLeaderId);
                boolean result = this.updateById(updateTeam);
                if (!result) {
                    throw new BusinessException(ErrorCode.SYSTEM_EXCEPTION, "更新队伍队长失败");
                }

            }
        }
        //     - 如果自己不是队长，就自己退出队伍
        return userTeamService.remove(new LambdaQueryWrapper<UserTeam>()
                .eq(UserTeam::getTeamId, teamId).eq(UserTeam::getUserId, userId));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean deleteTeam(long id, HttpServletRequest request) {
        Team team = getTeamById(id);
        // 我是否是该队伍的队长
        User loginUser = userService.getLoginUser(request);
        if (!team.getLeaderId().equals(loginUser.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH, "您无权解散该队伍！");
        }
        // 删除所有 用户-队伍 关联信息
        boolean result = userTeamService.remove(new LambdaQueryWrapper<UserTeam>().eq(UserTeam::getTeamId, id));
        if (!result) {
            throw new BusinessException(ErrorCode.SYSTEM_EXCEPTION, "删除队伍关联信息失败");
        }
        // 删除队伍
        return this.remove(new LambdaQueryWrapper<Team>().eq(Team::getId, id));

    }

    private Team getTeamById(long id) {
        // 1.  校验请求参数
        if (id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 2.  队伍是否存在
        Team team = this.getById(id);
        if (team == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "该队伍不存在！");
        }
        return team;
    }

    private long countUserByTeamId(Long teamId) {
        long userCount = userTeamService.count(new LambdaQueryWrapper<UserTeam>()
                .eq(UserTeam::getTeamId, teamId));
        return userCount;
    }
}




